home *** CD-ROM | disk | FTP | other *** search
- /* PR-GRAPH.C - library of printer graphics functions
-
- This supports an article in Micro Cornucopia Magazine Issue #45.
- As always, the code is up for grabs. In the immortal words of Laine Stump,
- "Permission to do whatever the hell you want with it." I do ask that if
- you use this code to become rich beyond your wildest dreams, please send
- me a six-pack of the best local brew in your neck of the woods.
-
- You'll notice the structure (of type plot) is different from the one
- printed in the mag. I originally wrote only the East version of the
- graphing routines. When I started the others I realized that it made more
- sense to define an axis in terms of its origin and axis lengths. Much
- easier to rotate a given axis to different orientations with it defined
- this way.
-
- I also fiddled with the axis labeling in p_draw_scale with less than
- wonderful results. If you choose axis lengths (x_length and y_length)
- judiciously there's no problem. Play with different axis lengths for a
- given plot and you'll see what I mean.
-
- Needs DUMP.C (from Issue #44) for printer setup, etc. The function
- setup_gr_line () in DUMP.C should be changed to accept (char p_mode,
- int width) as parameters. I originally wrote it with 60 dpi mode hard
- coded in.
-
- Requires the font file PR_CHARS.DAT generated by CHARGEN.C.
-
- Oh yeah, I used Turbo C v1.5 compact model.
-
- Larry Fogg - Halloween, 1988
- */
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <dos.h>
- #include <math.h> /* only included for sin () example */
- #include "dump.c" /* printer setup and Herc screen dump from Micro C #44 */
-
- #define boolean char
- #define yes 1
- #define no 0
-
- #define N 'N' /* "directions" on printer paper */
- #define E 'E' /* N is the direction of normal text output */
- #define W 'W'
- #define S 'S'
-
- #define herc_base 0xb000 /* Herc parameters */
- #define v_max_row 347
- #define v_max_col 719
- #define ch_size 11 /* dot highth of a printer char */
-
- typedef struct /* used for graph plotting routines */
- {
- char orientation; /* direction of X axis of graph */
- int x_min; /* actual axis origin coordinates (x-east, y-north) */
- int y_min;
- int x_length; /* actual length of axis */
- int y_length;
- char x_label [50]; /* axis labels */
- char y_label [50];
- int xdata_min; /* data bounds */
- int xdata_max;
- int ydata_min;
- int ydata_max;
- int x_step; /* increments for labeling axis */
- int y_step;
- int points [2][100]; /* data in (x, y) pairs */
- } plot;
-
- char *p_buf; /* pointer to printer memory */
- char p_chars [256][11]; /* holds 11 byte definition for each character */
-
- int p_max_line; /* printer memory boundaries */
- int p_max_col;
- unsigned p_buf_size;
- char p_mode; /* printer mode */
-
-
-
- boolean p_init (char p_m, int max_line, int max_col)
- {
- p_buf_size = (max_col + 1) * (((max_line+1)/8) + 1); /* alloc print memory */
- p_buf = malloc (p_buf_size);
- if (p_buf != NULL) /* malloc () returns null if it fails */
- {
- p_max_line = max_line; /* set global vars */
- p_max_col = max_col;
- p_mode = p_m;
- return (yes);
- }
- else
- return (no); /* not enough memory for printer memory */
- } /* p_init */
-
-
-
- void p_load_chars () /* load the character font from file */
- { /* this file created by CHARGEN.C */
- FILE *f;
-
- f = fopen ("PR_CHARS.DAT", "rb");
- fread (p_chars, 0xb00, 1, f);
- } /* p_load_chars */
-
-
-
-
-
-
- void p_clear_buf () /* clears printer memory */
- {
- unsigned i;
-
- for (i=0; i<p_buf_size; i++)
- *(p_buf+i) = 0;
- } /* p_clear_buf */
-
-
-
-
- void p_draw_point (int col, int line, boolean fill)
- {
- int i; /* offset within memory for byte containing the point */
- char mask; /* locates point within the byte */
-
- if ((col>=0) && (line>=0) && (col<=p_max_col) && (line<=p_max_line)) /* OK */
- {
- mask = 1 << (7 - (line % 8));
- i = (line/8)*(p_max_col+1) + col;
- if (fill) /* draw the point */
- *(p_buf+i)= *(p_buf+i) | mask;
- else /* erase the point */
- *(p_buf+i) = *(p_buf+i) & ~mask;
- }
- else; /* do nothing - coordinates out of bounds */
- } /* p_draw_point */
-
-
-
-
-
- void p_big_point (int col, int line, boolean fill)
- { /* draws point and its nine neighbors */
- int i, j;
-
- for (i=col-1; i<=col+1; i++)
- for (j=line-1; j<=line+1; j++)
- p_draw_point (i, j, fill);
- } /* p_big_point */
-
-
-
-
-
-
- void p_draw_line (int col1, int line1, int col2, int line2, boolean on)
- { /* uses Bresenham algorithm to draw a line */
- int dX, dY; /* vector components */
- int row, col,
- final, /* final row or column number */
- G, /* used to test for new row or column */
- inc1, /* G increment when row or column doesn't change */
- inc2; /* G increment when row or column changes */
- boolean pos_slope;
-
- dX = line2 - line1; /* find vector components */
- dY = col2 - col1;
- pos_slope = (dX > 0); /* is slope positive? */
- if (dY < 0)
- pos_slope = !pos_slope;
- if (abs (dX) > abs (dY)) /* shallow line case */
- {
- if (dX > 0) /* determine start point and last column */
- {
- col = line1;
- row = col1;
- final = line2;
- }
- else
- {
- col = line2;
- row = col2;
- final = line1;
- }
- inc1 = 2 * abs (dY); /* determine increments and initial G */
- G = inc1 - abs (dX);
- inc2 = 2 * (abs (dY) - abs (dX));
- if (pos_slope)
- while (col <= final) /* step through columns chacking for new row */
- {
- p_draw_point (row, col, on);
- col++;
- if (G >= 0) /* it's time to change rows */
- {
- row++; /* positive slope so increment through the rows */
- G += inc2;
- }
- else /* stay at the same row */
- G += inc1;
- }
- else
- while (col <= final) /* step through columns checking for new row */
- {
- p_draw_point (row, col, on);
- col++;
- if (G > 0) /* it's time to change rows */
- {
- row--; /* negative slope so decrement through the rows */
- G += inc2;
- }
- else /* stay at the same row */
- G += inc1;
- }
- } /* if |dX| > |dY| */
- else /* steep line case */
- {
- if (dY > 0) /* determine start point and last row */
- {
- col = line1;
- row = col1;
- final = col2;
- }
- else
- {
- col = line2;
- row = col2;
- final = col1;
- }
- inc1 = 2 * abs (dX); /* determine increments and initial G */
- G = inc1 - abs (dY);
- inc2 = 2 * (abs (dX) - abs (dY));
- if (pos_slope)
- while (row <= final) /* step through rows checking for new column */
- {
- p_draw_point (row, col, on);
- row++;
- if (G >= 0) /* it's time to change columns */
- {
- col++; /* positive slope so increment through the columns */
- G += inc2;
- }
- else /* stay at the same column */
- G += inc1;
- }
- else
- while (row <= final) /* step throuth rows checking for new column */
- {
- p_draw_point (row, col, on);
- row++;
- if (G > 0) /* it's time to change columns */
- {
- col--; /* negative slope so decrement through the columns */
- G += inc2;
- }
- else /* stay at the same column */
- G += inc1;
- }
- }
- } /* p_draw_line */
-
-
-
-
-
- void p_put_c (unsigned char ch, int col, int line, char direction)
- {
- int r, c;
-
- switch (direction)
- {
- case E: for (r=0; r<11; r++)
- for (c=0; c<8; c++)
- p_draw_point (col+r, line+c, p_chars [ch][r] & (1 << (7-c)));
- break;
- case W: for (r=0; r<11; r++)
- for (c=0; c<8; c++)
- p_draw_point (col-r, line-c, p_chars [ch][r] & (1 << (7-c)));
- break;
- case N: for (r=0; r<11; r++)
- for (c=0; c<8; c++)
- p_draw_point (col+c, line-r, p_chars [ch][r] & (1 << (7-c)));
- break;
- case S: for (r=0; r<11; r++)
- for (c=0; c<8; c++)
- p_draw_point (col-c, line+r, p_chars [ch][r] & (1 << (7-c)));
- break;
- } /* switch */
- } /* p_put_c */
-
-
-
-
- void p_put_s (unsigned char str_out [80], int col, int line, char direction)
- {
- int i;
-
- i = 0;
- switch (direction)
- {
- case E: while (str_out [i] != '\0')
- {
- p_put_c (str_out [i], col, line + 9*i, E);
- i++;
- }
- break;
- case W: while (str_out [i] != '\0')
- {
- p_put_c (str_out [i], col, line - 9*i, W);
- i++;
- }
- break;
- case N: while (str_out [i] != '\0')
- {
- p_put_c (str_out [i], col + 9*i, line, N);
- i++;
- }
- break;
- case S: while (str_out [i] != '\0')
- {
- p_put_c (str_out [i], col - 9*i, line, S);
- i++;
- }
- break;
- } /* switch */
- } /* p_put_s */
-
-
-
-
- void p_print_buf () /* print out contents of printer memory */
- {
- int col, line, total_lines;
-
- total_lines = p_max_line/8 + 1; /* # passes for the printer head */
- prn_setup ();
- for (line=0; line<total_lines; line++)
- {
- setup_gr_line (p_mode, p_max_col+1);
- for (col=0; col<=p_max_col; col++) /* send graphics line */
- prn_out (*(p_buf + (unsigned)(line*(p_max_col+1) + col)));
- prn_out (10); /* print it */
- }
- } /* p_print_buf */
-
-
-
-
-
- void p_draw_border (int col1, int line1, int col2, int line2)
- {
- p_draw_line (col1, line1, col2, line1, yes);
- p_draw_line (col2, line1, col2, line2, yes);
- p_draw_line (col2, line2, col1, line2, yes);
- p_draw_line (col1, line2, col1, line1, yes);
- } /* p_draw_border */
-
-
-
-
-
- /* This function reads Hercules graphics page 0 and stuffs it in the printer
- memory. Position the video graphics within the printer page using the two
- offset parameters. Note that a line_ofs of 1 is really 8 printer lines. I
- didn't want to fool with "uneven" line offsets. Be sure there's room for
- the screen - the function doesn't check. */
-
- void p_get_screen (int col_ofs, int line_ofs)
- {
- unsigned buf_index; /* offset from base of printer memory */
- int byte_ofs, /* offset from base of video memory */
- vid_col, vid_row, blanks;
-
- blanks = p_max_col - v_max_row; /* skip this much between video columns */
- buf_index = line_ofs*(p_max_col + 1) + col_ofs; /*video starts filling here*/
- for (vid_col=0; vid_col<v_max_col; vid_col+=8, buf_index+=blanks)
- for (vid_row=v_max_row; vid_row>=0; vid_row--, buf_index++)
- {
- byte_ofs = 0x2000*(vid_row % 4) + 90*(vid_row/4) + (vid_col/8);
- *(p_buf + buf_index) = peekb (herc_base, byte_ofs); /*video to printer*/
- }
- } /* p_get_screen */
-
-
-
-
-
- void p_draw_axis (plot *plt) /* draw axis and print text labels */
- {
- switch (plt->orientation)
- {
- case E: p_draw_line (plt->y_min, plt->x_min,
- plt->y_min, plt->x_min + plt->x_length, yes);
- p_draw_line (plt->y_min, plt->x_min,
- plt->y_min + plt->y_length, plt->x_min, yes);
- p_put_s (plt->x_label, plt->y_min-3*ch_size, plt->x_min+15, E);
- p_put_s (plt->y_label, plt->y_min+15, plt->x_min-2*ch_size, N);
- break;
- case W: p_draw_line (plt->y_min, plt->x_min,
- plt->y_min, plt->x_min - plt->x_length, yes);
- p_draw_line (plt->y_min, plt->x_min,
- plt->y_min - plt->y_length, plt->x_min, yes);
- p_put_s (plt->x_label, plt->y_min+3*ch_size, plt->x_min-15, W);
- p_put_s (plt->y_label, plt->y_min-15, plt->x_min+2*ch_size, S);
- break;
- case N: p_draw_line (plt->y_min, plt->x_min,
- plt->y_min, plt->x_min - plt->x_length, yes);
- p_draw_line (plt->y_min, plt->x_min,
- plt->y_min + plt->y_length, plt->x_min, yes);
- p_put_s (plt->x_label, plt->y_min+15, plt->x_min+3*ch_size, N);
- p_put_s (plt->y_label, plt->y_min-2*ch_size, plt->x_min-15, W);
- break;
- case S: p_draw_line (plt->y_min, plt->x_min,
- plt->y_min, plt->x_min + plt->x_length, yes);
- p_draw_line (plt->y_min, plt->x_min,
- plt->y_min - plt->y_length, plt->x_min, yes);
- p_put_s (plt->x_label, plt->y_min-15, plt->x_min-3*ch_size, S);
- p_put_s (plt->y_label, plt->y_min+2*ch_size, plt->x_min+15, E);
- break;
- } /* switch */
- } /* p_draw_axis */
-
-
-
-
-
- void p_draw_scale (plot *plt)
- {
- int x, y, /* current drawing location (x-east, y-north) */
- x_label, y_label; /* incremental axis labels */
- float xdata_span, ydata_span, /* data range (float forces real division) */
- dx, dy; /* axis increments */
- char temp [6]; /* x_label and y_label in string form */
-
- xdata_span = plt->xdata_max - plt->xdata_min; /* find data range */
- ydata_span = plt->ydata_max - plt->ydata_min;
- dx = (plt->x_step/xdata_span) * plt->x_length + 0.5; /*find axis increments*/
- dy = (plt->y_step/ydata_span) * plt->y_length + 0.5;
- switch (plt->orientation)
- {
- case E: x = plt->x_min; /* initial values */
- x_label = plt->xdata_min;
- while (x <= plt->x_min + plt->x_length)
- {
- p_draw_point (plt->y_min - 1, x, yes); /* draw "tick" on axis */
- p_draw_point (plt->y_min - 2, x, yes);
- itoa (x_label, temp, 10); /* convert tick label to string */
- p_put_s (temp, plt->y_min - 16, x - 4, E); /* label the tick */
- x_label += plt->x_step; /* set up for next tick */
- x += dx;
- }
- y = plt->y_min; /* same for y-axis */
- y_label = plt->ydata_min;
- while (y <= plt->y_min + plt->y_length)
- {
- p_draw_point (y, plt->x_min - 1, yes);
- p_draw_point (y, plt->x_min - 2, yes);
- itoa (y_label, temp, 10);
- p_put_s (temp, y - 4, plt->x_min - 5, N);
- y_label += plt->y_step;
- y += dy;
- }
- break;
- case W: x = plt->x_min; /* initial values */
- x_label = plt->xdata_min;
- while (x >= plt->x_min - plt->x_length)
- {
- p_draw_point (plt->y_min + 1, x, yes); /* draw "tick" on axis */
- p_draw_point (plt->y_min + 2, x, yes);
- itoa (x_label, temp, 10); /* convert tick label to string */
- p_put_s (temp, plt->y_min + 16, x + 4, W); /* label the tick */
- x_label += plt->x_step; /* set up for next tick */
- x -= dx;
- }
- y = plt->y_min; /* same for y-axis */
- y_label = plt->ydata_min;
- while (y >= plt->y_min - plt->y_length)
- {
- p_draw_point (y, plt->x_min + 1, yes);
- p_draw_point (y, plt->x_min + 2, yes);
- itoa (y_label, temp, 10);
- p_put_s (temp, y + 4, plt->x_min + 5, S);
- y_label += plt->y_step;
- y -= dy;
- }
- break;
- case N: y = plt->y_min; /* initial values */
- x_label = plt->xdata_min;
- while (y <= plt->y_min + plt->x_length)
- {
- p_draw_point (y, plt->x_min + 1, yes); /* draw "tick" on axis */
- p_draw_point (y, plt->x_min + 2, yes);
- itoa (x_label, temp, 10); /* convert tick label to string */
- p_put_s (temp, y - 4, plt->x_min + 16, N); /* label the tick */
- x_label += plt->x_step; /* set up for next tick */
- y += dx;
- }
- x = plt->x_min; /* same for y-axis */
- y_label = plt->ydata_min;
- while (x >= plt->x_min - plt->y_length)
- {
- p_draw_point (plt->y_min - 1, x, yes);
- p_draw_point (plt->y_min - 2, x, yes);
- itoa (y_label, temp, 10);
- p_put_s (temp, plt->y_min - 5, x + 4, W);
- y_label += plt->y_step;
- x -= dy;
- }
- break;
- case S: y = plt->y_min; /* initial values */
- x_label = plt->xdata_min;
- while (y >= plt->y_min - plt->x_length)
- {
- p_draw_point (y, plt->x_min - 1, yes); /* draw "tick" on axis */
- p_draw_point (y, plt->x_min - 2, yes);
- itoa (x_label, temp, 10); /* convert tick label to string */
- p_put_s (temp, y + 4, plt->x_min - 16, S); /* label the tick */
- x_label += plt->x_step; /* set up for next tick */
- y -= dx;
- }
- x = plt->x_min; /* same for y-axis */
- y_label = plt->ydata_min;
- while (x <= plt->x_min + plt->y_length)
- {
- p_draw_point (plt->y_min + 1, x, yes);
- p_draw_point (plt->y_min + 2, x, yes);
- itoa (y_label, temp, 10);
- p_put_s (temp, plt->y_min + 5, x - 4, E);
- y_label += plt->y_step;
- x += dy;
- }
- break;
- } /* switch */
- } /* p_draw_scale */
-
-
-
-
- void p_draw_data (plot *plt)
- {
- int i;
- float xdata_span, ydata_span; /* data range (float forces real division) */
-
- xdata_span = plt->xdata_max - plt->xdata_min; /* find data range */
- ydata_span = plt->ydata_max - plt->ydata_min;
- switch (plt->orientation)
- {
- case E: for (i=1; i<=plt->points [0][0]; i++) /* 0,0 has # of points */
- p_big_point (plt->y_min +
- (plt->points [1][i]/ydata_span)*plt->y_length + 0.5,
- plt->x_min +
- (plt->points [0][i]/xdata_span)*plt->x_length + 0.5,
- yes);
- break;
- case W: for (i=1; i<=plt->points [0][0]; i++) /* 0,0 has # of points */
- p_big_point (plt->y_min -
- (plt->points [1][i]/ydata_span)*plt->y_length + 0.5,
- plt->x_min -
- (plt->points [0][i]/xdata_span)*plt->x_length + 0.5,
- yes);
- break;
- case N: for (i=1; i<=plt->points [0][0]; i++) /* 0,0 has # of points */
- p_big_point (plt->y_min +
- (plt->points [1][i]/ydata_span)*plt->y_length + 0.5,
- plt->x_min -
- (plt->points [0][i]/xdata_span)*plt->x_length + 0.5,
- yes);
- break;
- case S: for (i=1; i<=plt->points [0][0]; i++) /* 0,0 has # of points */
- p_big_point (plt->y_min -
- (plt->points [1][i]/ydata_span)*plt->y_length + 0.5,
- plt->x_min +
- (plt->points [0][i]/xdata_span)*plt->x_length + 0.5,
- yes);
- break;
- } /* switch */
- } /* p_draw_data */
-
-
-
-
- void p_draw_plot (plot *plt)
- {
- p_draw_axis (plt);
- p_draw_scale (plt);
- p_draw_data (plt);
- } /* p_draw_plt */
-
-
-
-
-
- void graph_test () /* example of graph use */
- { /* use 705 x 470 in p_init call in main */
- int i;
- plot *p;
-
- p = (plot *) malloc (sizeof (plot)); /* get memory for structure */
- p->orientation = E; /* load structure values */
- p->x_min = 35; p->x_length = p_max_line - p->x_min - 25;
- p->y_min = 35; p->y_length = p_max_col - p->y_min - 25;
- strcpy (p->x_label, "Sample PR-GRAPH Output - (X)");
- strcpy (p->y_label, "150+100*sin(X/4)");
- p->xdata_min = 0; p->xdata_max = 100;
- p->ydata_min = 0; p->ydata_max = 300;
- p->x_step = 20;
- p->y_step = 100;
- p->points [0][0] = 99; /* element 0,0 holds # of data points */
- for (i=1; i<=99; i++) /* load data array */
- {
- p->points [0][i] = i; /* x value */
- p->points [1][i] = 150 + 100*sin(i/4.0); /* y value */
- }
- p_draw_plot (p); /* do the graph */
- p_draw_border (0, 0, p_max_col, p_max_line);
- p_print_buf (); /* print it */
- } /* graph_test */
-
-
-
-
-
- void graph_test4 () /* example of four graph orientations */
- { /* use 450 x 450 in p_init call in main */
- int i;
- plot *p;
-
- p = (plot *) malloc (sizeof (plot)); /* get memory for structure */
-
- p->orientation = E; /* load structure values for E plot */
- p->x_min = 275; p->x_length = 150;
- p->y_min = 275; p->y_length = 150;
- strcpy (p->x_label, "EAST X-axis");
- strcpy (p->y_label, "EAST Y-axis");
- p->xdata_min = 0; p->xdata_max = 10;
- p->ydata_min = 0; p->ydata_max = 100;
- p->x_step = 2;
- p->y_step = 25;
- p->points [0][0] = 10; /* element 0,0 holds # of data points */
- for (i=1; i<=10; i++) /* load data array */
- {
- p->points [0][i] = i; /* x value */
- p->points [1][i] = 10*i; /* y value */
- }
- p_draw_plot (p); /* do the graph */
-
- p->orientation = W; /* load new W values */
- p->x_min = 175;
- p->y_min = 175;
- strcpy (p->x_label, "WEST X-axis");
- strcpy (p->y_label, "WEST Y-axis");
- p_draw_plot (p); /* do the graph */
-
- p->orientation = N; /* load new N values */
- p->x_min = 175;
- p->y_min = 275;
- strcpy (p->x_label, "NORTH X-axis");
- strcpy (p->y_label, "NORTH Y-axis");
- p_draw_plot (p); /* do the graph */
-
- p->orientation = S; /* load new S values */
- p->x_min = 275;
- p->y_min = 175;
- strcpy (p->x_label, "SOUTH X-axis");
- strcpy (p->y_label, "SOUTH Y-axis");
- p_draw_plot (p); /* do the graph */
-
- p_draw_border (0, 0, p_max_col, p_max_line);
- p_print_buf (); /* print it */
- } /* graph_test4 */
-
-
-
-
-
- main ()
- {
- if (p_init (75, 450, 450))
- {
- p_clear_buf ();
- p_load_chars ();
- /* graph_test ();*/
- graph_test4 ();
- }
- else
- printf ("\n\n Not enough memory ...\n\n");
- free (p_buf); /* retreive printer memory */
- }
-